#pragma once
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include "../comm/log.hpp"
#include "../comm/util.hpp"

namespace runners
{
    using namespace util;
    using namespace Log;
    class Runner
    {    
        Runner() {}
        ~Runner() {}
    public:
        static void SetProcLimit(int _cpu,int _mem)
        {
            struct rlimit cpu_rlimit;
            cpu_rlimit.rlim_max = RLIM_INFINITY;
            cpu_rlimit.rlim_cur = _cpu;
            setrlimit(RLIMIT_CPU, &cpu_rlimit);

            struct rlimit mem_rlimit;
            mem_rlimit.rlim_max = RLIM_INFINITY;
            mem_rlimit.rlim_cur = _mem * 1024;//kb  
            setrlimit(RLIMIT_AS,&mem_rlimit);
        }
        static int Run(const string &file_name, int cpu_limit, int mem_limit)
        {
            std::string _execute = util::PathUtil::Exe(file_name);
            std::string _stdin = util::PathUtil::Stdin(file_name);
            std::string _stdout = util::PathUtil::Stdout(file_name);
            std::string _stderr = util::PathUtil::Stderror(file_name);
            umask(0);
            int _stdin_fd = open(_stdin.c_str(), O_CREAT | O_RDONLY, 0644);
            int _stdout_fd = open(_stdout.c_str(), O_CREAT | O_WRONLY, 0644);
            int _stderr_fd = open(_stderr.c_str(), O_CREAT | O_WRONLY, 0644);
            if (_stdin_fd < 0 || _stdout_fd < 0 || _stderr_fd < 0)
            {
                LOG(ERROR) << "运⾏时打开标准⽂件失败" << "\n";
                return -1; // 代表打开⽂件失败
            }
            pid_t pid = fork();
            if (pid < 0)
            {
                LOG(ERROR) << "创建子进程失败" << "\n";
                close(_stdin_fd);
                close(_stdout_fd);
                close(_stderr_fd);
                return -2; // 代表创建⼦进程失败
            }
            else if (pid == 0)
            {
                dup2(_stdin_fd, 0);
                dup2(_stdout_fd, 1);
                dup2(_stderr_fd, 2);
                SetProcLimit(cpu_limit, mem_limit);
                execl(PathUtil::Exe(file_name).c_str() /*我要执⾏谁*/, PathUtil::Exe(file_name).c_str() /*我想在命令⾏上如何执⾏该程序*/, nullptr);
                exit(1);
            }
            else
            {
                close(_stdin_fd);
                close(_stdout_fd);
                close(_stderr_fd);
                int status = 0;
                waitpid(pid, &status, 0);
                LOG(INFO) << "运⾏完毕, info: " << (status & 0x7F) << "\n";
                return status & 0x7F;
                /************************************************************************************************************************************
                 * waitpid 通过输出参数 status 返回子进程的终止状态，这个 int 类型的变量被分段编码,高字节和低字节分别存储不同信息
                 * 低 7 位（bit 0~6）：存储 “子进程是否因信号终止” 及具体的信号编号（如 SIGSEGV 段错误是 11，SIGKILL 强制杀死是 9）。
                 * 若这 7 位非 0，说明子进程是被信号杀死的（异常终止）。
                 * 高字节及其他位：存储 “子进程正常退出时的退出码”（如 exit(1) 中的 1）、核心转储标志等其他状态。
                 *
                 * 0x7F 是二进制 00000000 01111111（假设 32 位系统，高位均为 0），它的作用是 “屏蔽 status 中除低 7 位以外的所有位”，只保留 “信号编号相关的信息”：
                 * 如果子进程是被信号终止的（如段错误、超时），status & 0x7F 会得到具体的信号编号（1~31，因为 Linux 常用信号编号在这个范围，且低 7 位足够容纳）；
                 * 如果子进程正常退出（如 exit(0)），status & 0x7F 结果为 0（表示没有信号终止）。
                 ************************************************************************************************************************************/
            }
            return 0;
        }
    };
}
